[BITS 32]
section .text
    global _supportsSSE2
    global __InterpolateBilinearSSE2

; int supportsSSE2()
_supportsSSE2:
    push dword ebx ; ebx is not a scratch register and must be saved
    mov eax, 1
    cpuid
    mov eax, edx
    and eax, 04000000h
    pop dword ebx
    ret

bl_notest:
    pxor xmm7, xmm7 ; xmm7 is zeroset
    pxor xmm0, xmm0 ; xmm0 is ret

    ; repackage mods
    movdqa xmm3, xmm5
    pslld xmm3, 16
    por xmm5, xmm3
    pshufd xmm3, xmm5, 0xFA ; xmm3[0..1] = xmm5[2], xmm3[2..3] = xmm5[3]
    pshufd xmm5, xmm5, 0x50 ; xmm5[0..1] = xmm5[0], xmm5[2..3] = xmm5[1]

    movdqa xmm4, xmm6 ; xmm4 is second two pels
    punpckhbw xmm4, xmm7 ;
    punpcklbw xmm6, xmm7 ; xmm6 is first two pels

    ; multiply pixel channels by mod
    pmullw xmm6, xmm5 ; first set
    pmullw xmm4, xmm3 ; second set
    ; add modified channels to result
    paddw xmm0, xmm6
    paddw xmm0, xmm4
bl_end:
    pshufd xmm1, xmm0, 0x0E ;
    paddw xmm0, xmm1 ; *(uint32_t *)xmm0[0..1] += *(uint32_t *)xmm0[2..3]
    psrlw xmm0, 8 ; retchans >>= 8
    packuswb xmm0, xmm7
    movd eax, xmm0
    ret

; Pixel __cdecl interpolateSSE2b(Pixel *pels, Half *mods, int testmode, Pixel colorkey)
; esp+4 = pels esp+8 = mods esp+12 = testmode esp+16 = colorkey
__InterpolateBilinearSSE2:
    mov edx, [esp + 4]
    movdqu xmm6, [edx] ; xmm6 is pels
    mov edx, [esp + 8]
    movdqu xmm5, [edx] ; xmm5 is mods

    mov eax, [esp+12]
    cmp eax, 0
    jz bl_notest
        pxor xmm7, xmm7 ; xmm7 is test results; test results all 0
        test eax, 2 ; alpha test
        jz colorkeytest
            mov edx, 0xFF000000 ; alpha mask
            movd xmm1, edx
            pshufd xmm1, xmm1, 0 ; xmm1[0..3] = alphamask
            pand xmm1, xmm6 ; xmm1[0..3] = xmm6[0..3] & alphamask
            pxor xmm0, xmm0 ; xmm1[0..3] = 0
            pcmpeqd xmm1, xmm0 ; xmm1[0..3] = xmm1[0..3] == 0
            por xmm7, xmm1 ; xmm7[0..3] |= (pels[0..3] & alphamask) == 0
        colorkeytest:
        test eax, 1 ; colorkey test
        jz testfinal
            mov edx, [esp + 16]
            movd xmm1, edx
            pshufd xmm1, xmm1, 0 ; xmm1[0..3] = colorkey
            pcmpeqd xmm1, xmm6  ; xmm0[0..3] = xmm0[0..3] == colorkey
            por xmm7, xmm1 ; xmm7[0..3] |= pels[0..3] == colorkey
        testfinal:
        ; we need to determine whether or not we need to consider these results
        ; so we need to pack results into a 32-bit register and test for != 0
        movdqa xmm0, xmm7
        pxor xmm1, xmm1
        packssdw xmm0, xmm1
        packsswb xmm0, xmm1
        movd eax, xmm0
        cmp eax, 0
        jz bl_notest ; jump if all tests passed

        ; we need to average passed pixels to compute final result ;

        ; xmm7 currently holds failed results, we want passed results from here
        mov edx, 0xFFFFFFFF ; all bits set to match output of pcmpeqd
        movd xmm0, edx
        pshufd xmm0, xmm0, 0 ; all bits in xmm0 set
        pxor xmm7, xmm0 ; xmm7[0..3] = ~xmm7[0..3] = !xmm7[0..3]

        ; pcmpeqd sets all bits, so we can just and pels by the result
        pand xmm6, xmm7 ; pels[0..3] &= test[0..3]
        ; do the same to find merged proportions
        movdqa xmm2, xmm5
        pand xmm2, xmm7 ; xmm2 = merged

        ; now we want the number of tests passed
        mov edx, 1
        movd xmm0, edx
        pshufd xmm0, xmm0, 0 ; xmm0[0..3] = 1
        pand xmm7, xmm0 ; xmm7[0..3] &= 1
        pshufd xmm0, xmm7, 0x0E ; xmm0[0..1] = xmm7[2..3]
        paddd xmm7, xmm0;
        pshufd xmm0, xmm7, 0x01 ; xmm0[0] = xmm7[1]
        paddd xmm7, xmm0
        movd ecx, xmm7 ; ecx = xmm7[0]&0xFF|(xmm7[1] & 0xFF << 8)|(xmm7[2]&0xFF<<16)|(xmm7[3]&0xff<<24)
        cmp ecx, 0
        jnz dowork
            mov eax, [esp + 16] ; eax = colorkey
            ret
        dowork:

        pxor xmm0, xmm0 ; xmm0 = result channels
        pxor xmm1, xmm1 ; xmm1 = average channels
        pxor xmm7, xmm7 ; xmm7 is our zeroset

        ; repackage mods
        movdqa xmm3, xmm5
        pslld xmm3, 16
        por xmm5, xmm3
        pshufd xmm3, xmm5, 0xFA ; xmm3[0..1] = xmm5[2], xmm3[2..3] = xmm5[3]
        pshufd xmm5, xmm5, 0x50 ; xmm5[0..1] = xmm5[0], xmm5[2..3] = xmm5[1]

        movdqa xmm4, xmm6 ; xmm4 is second two pels
        punpckhbw xmm4, xmm7 ;
        punpcklbw xmm6, xmm7 ; xmm6 is first two pels

        ; add pixel channels to average
        paddw xmm1, xmm4
        paddw xmm1, xmm6

        ; multiply pixel channels by mod
        pmullw xmm6, xmm5 ; first set
        pmullw xmm4, xmm3 ; second set
        ; add modified channels to result
        paddw xmm0, xmm6
        paddw xmm0, xmm4

        ; compute the average
        pshufd xmm3, xmm2, 0x0E ; xmm3[0..1] = xmm2[2..3]
        paddd xmm2, xmm3 ; xmm2[0..1] += xmm2[2..3]
        pshufd xmm3, xmm2, 0x01; xmm3[0] = xmm2[1]
        paddd xmm2, xmm3 ; xmm2[0] += xmm2[1] :. xmm2[0] += xmm2[1..3]
        movd eax, xmm2
        neg eax
        add eax, 256
        cdq ; sign extend eax into edx (set edx to 0)
        idiv ecx ; eax = unmerged/numavg
        movd xmm2, eax
        pshuflw xmm2, xmm2, 0 ; xmm2[0..3] = xmm2[0]
        pshufd xmm2, xmm2, 0 ; xmm2[4..7] = xmm2[0]
        pmullw xmm1, xmm2 ; xmm1[0..7] *= unmerged/numavg
        pinsrw xmm1, edx, 3 ; clear alpha
        pinsrw xmm1, edx, 7 ; clear alpha

        paddw xmm0, xmm1
        jmp bl_end
